
#define COMMUNICATE_PORT 8888
#define SERIAL_LOG true
#define I2C_SDA 5
#define I2C_SCL 4
#define LEDPIN 1
#define OLED_FPS 120

const char *APssid = "UmekoLab";
const char *APpassword = "UmekoLab";
// char ip_string[15];
#include <WiFiUdp.h>
#include "wifi_communicate.h"
#include "soc/soc.h"           //disable brownout problems
#include "soc/rtc_cntl_reg.h"  //disable brownout problems
#include "state_led.h"
#include <U8g2lib.h>
#include "oled_animation_menu.h"
#include "web_page.h"


U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ I2C_SCL, /* data=*/ I2C_SDA);
// 定义任务句柄，可以通过句柄与其它任务通信
TaskHandle_t wifiStartupTaskHandle;
TaskHandle_t udpCmdTaskHandle;
TaskHandle_t restartTaskHandle;
TaskHandle_t oledTaskHandle;
TaskHandle_t webServerHandle;


WiFiUDP udp1;
UDP_Communicator udp_com(&udp1);
LED_CONTRLLER led_controller(LEDPIN);
QuePrinter web_printer;

bool udp_online = false;
char incomingPacket[255]="Umeko Lab";
char lastFrom[15]="Serial 0";
bool flag_fresh_oled=true;
const char *now_IP;
uint8_t Pins[] = {0,2,3,6,7,8,9,12,13,10,11,18,19};

const size_t Num_elements = sizeof(Pins) / sizeof(Pins[0]);
uint8_t Pin_states[Num_elements] = {};



void init_all_pins(){
    for (size_t i = 0; i < Num_elements; ++i) {
        pinMode(Pins[i], OUTPUT);
    }
}

// 判断管脚是否处于可控列表中
bool num_in_pin_list(uint8_t num){
    for (size_t i = 0; i < Num_elements; ++i) {
        if (Pins[i] == num){
            return true;
        }
    }
    return false;
}

void print_all_state(Print * serial){
    String out;
    for (size_t i = 0; i < Num_elements; ++i) {
        out = out + String(Pins[i]) + " :" + String(Pin_states[Pins[i]]) + "\n";
    }
    serial->print(out);
}

// 处理指令字串
void reqHandler(String req, Print * serial){
    if (req.substring(0, 1) == ":"){
        uint8_t PinID = req.substring(1).toInt();
        uint8_t state = req.substring(3).toInt();
        if (num_in_pin_list(PinID)){
            digitalWrite(PinID, state);
            serial -> printf("Pin %d set %d done!\n", PinID, state);
            Pin_states[PinID] = state;
        }
    }else if(req.substring(0, 1) == "!"){
        serial->print(req.substring(1));
        if (req.substring(1)=="RESTART"){
            serial->printf("System will be RESTART at %d mils\n", 1000);
            xTaskNotifyGive(restartTaskHandle);
        }
    }else if (req.substring(0, 1) == "?"){
        print_all_state(serial);
    }else if (req.substring(0, 1) == "$"){
        uint8_t setType = req.substring(1).toInt();
        if (setType == SET_IP){
            udp_com.set_remote_ip(req.substring(3).c_str());
            serial -> printf("RemoteIP set to \"%s\"\n", req.substring(3).c_str());
        }else if(setType == SET_SSID){
            udp_com.set_ssid(req.substring(3).c_str());
            serial -> printf("SSID set to \"%s\"\n", req.substring(3).c_str());
        }else if(setType == SET_PASSWORD){
            udp_com.set_password(req.substring(3).c_str());
            serial -> printf("PassWord set to \"%s\"\n", req.substring(3).c_str());
        }else if(setType == BROADCAST){
            udp_com.broadcast_string(req.substring(3));
        }else if(setType == SENDMSG){
            udp_com.write_string(req.substring(3));
        }else if(setType == LOG_WIFI){
            udp_com.log_output(serial);
        }
    }
}

void wifi_startup_task(void* parameter) {
    TickType_t xStartTime = xTaskGetTickCount();
    const TickType_t xWait = 5000;
    u8g2.clearBuffer();
    connecting_menu[1] = new char[20];
    sprintf(connecting_menu[1], "SSID: %s",udp_com.get_ssid());
    change_page(&u8g2, state_menu, connecting_menu);
    // render_page(&u8g2, connecting_menu, 0, 0);
    // u8g2.drawStr(10, 30, "Wifi connecting ...");
    // u8g2.sendBuffer();
    while(xStartTime + xWait > xTaskGetTickCount()){
        if(udp_com.wifi_startup()){
            udp_online = true;
            led_controller.setPeriod(1000);
            now_IP = strdup(WiFi.localIP().toString().c_str());
            xTaskNotifyGive(udpCmdTaskHandle);
            connecting_menu[2] = "Connected!";
            connecting_menu[3] = new char[20];
            sprintf(connecting_menu[3], "IP: %s", now_IP);
            render_page(&u8g2, connecting_menu, 0, 0);
            u8g2.sendBuffer();
            
            break;
        }
        vTaskDelay(10);
    }
    if (!udp_online){
        WiFi.softAP(APssid, APpassword);
        Serial.println("Faild to connect Wifi.");
        Serial.printf("AP started at: \"%s\"\n", APssid);
        Serial.print("UDP server started at ");
        Serial.print(WiFi.softAPIP());
        Serial.printf(":%d\n", COMMUNICATE_PORT);
        udp_online = true;
        xTaskNotifyGive(udpCmdTaskHandle);
        connecting_menu[2] = "Failed!";
        connecting_menu[3] = new char[25];
        now_IP = strdup(WiFi.softAPIP().toString().c_str());
        // strcpy(ip_string, WiFi.softAPIP().toString().c_str());
        sprintf(connecting_menu[3], "Start AP; IP: %s", now_IP);
        // u8g2.clearBuffer();
        render_page(&u8g2, connecting_menu, 0, 0);
        u8g2.sendBuffer();

    }
    vTaskDelay(1000);
    xTaskNotifyGive(oledTaskHandle);
    xTaskNotifyGive(webServerHandle);
    vTaskDelete(NULL); 
}

void task_UpdCmd(void * ptr){
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    udp_com.broadcast_string("Matrix Online!");
    led_controller.setMode(FADE);
    for(;;){
        int packetSize = udp1.parsePacket();
        if (packetSize) {
            int len = udp1.read(incomingPacket, 255);
            if (len > 0) {
            incomingPacket[len] = 0;
            }
            Serial.print("Received packet from ");
            Serial.print(udp1.remoteIP());
            Serial.print(": \"");
            Serial.print(incomingPacket);
            Serial.println("\"");
            // sprintf(lastFrom, "%s", udp1.remoteIP().toString().c_str());
            strcpy(lastFrom, udp1.remoteIP().toString().c_str());
            flag_fresh_oled = true;
            reqHandler(String(incomingPacket), &udp_com);
        }
        vTaskDelay(10);
    }
}

// 软重启任务
void task_restart(void * ptr){
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    TickType_t xLastWakeTime = xTaskGetTickCount();
    const TickType_t xFrequency = 1000;
    if (SERIAL_LOG) {
        Serial.printf("\nSystem will be RESTART at %d mils\n", xFrequency);
    }
    vTaskDelayUntil(&xLastWakeTime, xFrequency);
    ESP.restart();
}

void task_req_analyze(void * ptr){
    String req;
    while (true){
        req = "";
        if (Serial.available()){
            req = Serial.readStringUntil('\n');
            sprintf(incomingPacket, "%s", req.c_str());
            strcpy(lastFrom, "Serial0");
            flag_fresh_oled = true;
            reqHandler(req, &Serial);
        }
        vTaskDelay(10);
    }
}

void led_task(void * ptr){
    for(;;){
        led_controller.mainloop();
    }
}

void oled_task(void * ptr){
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    uint32_t xFrequency = 1000 / OLED_FPS;
    char ssid_status_string[30];
    char ip_status_string[20];
    char remote_status_string[20];
    if(udp_com.connected){
        // char* ssid = udp_com.get_ssid();
        sprintf(ssid_status_string,  "WIFI SSID: %s",  udp_com.get_ssid());
    }else{
        // char* ssid = (char*)APssid;
        sprintf(ssid_status_string,  "WIFI SSID: %s",  APssid);
    }
    sprintf(ip_status_string,  "IP: %s", now_IP);
    sprintf(remote_status_string,  "From %s:",  lastFrom);
    state_menu[0] = ssid_status_string;
    state_menu[1] = ip_status_string;
    state_menu[2] = remote_status_string;
    change_page(&u8g2, connecting_menu, state_menu, 1000);
    for(;;){
        if(flag_fresh_oled){
            sprintf(remote_status_string,  "From %s:",  lastFrom);
            u8g2.clearBuffer();
            render_page(&u8g2, state_menu, 0, 0);
            u8g2print_auto_newline(&u8g2, incomingPacket, 20, 44, 10);
            u8g2.sendBuffer();
            flag_fresh_oled = false;
        }
    vTaskDelay(xFrequency);
    }
}

void web_task(void * ptr){
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    init_web(now_IP);
    for(;;){
        server_handleClient();
        if(strlen(text_msg_str) != 0){
            flag_fresh_oled = true;
            sprintf(incomingPacket, "%s", text_msg_str);
            strcpy(lastFrom, web_remote_IP);
            strcpy(text_msg_str, "");
            reqHandler(String(incomingPacket), &web_printer);
        }
        web_printer.update();
        vTaskDelay(10);
    }
}

void setup(){
    // 禁止低电压重启
    WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
    Serial.begin(115200);
    Serial.setTimeout(5);
    u8g2.begin();
    u8g2.setFont(u8g2_font_ncenB08_tr);
    init_all_pins();
    udp_com.load_SSID_EPROM();
    udp_com.load_PSWD_EPROM();
    udp_com.load_rIP_EPROM();
    led_controller.setMode(BLINK);
    xTaskCreate(wifi_startup_task, "START_WIFI", 1024 * 6, NULL, 1, &wifiStartupTaskHandle); 
    xTaskCreate(led_task, "LED_TASK", 1024, NULL, 1, NULL); 
    xTaskCreate(oled_task, "OLED_TASK", 1024 * 2, NULL, 1, &oledTaskHandle); 
    xTaskCreate(task_UpdCmd, "UDP_CMD", 1024 * 3, NULL, 1, &udpCmdTaskHandle);
    xTaskCreate(task_req_analyze, "REQ_ANALYZE",     1024 * 6, NULL, 1, NULL);
    xTaskCreate(task_restart, "RESTART",             1024 * 2,     NULL, 1, &restartTaskHandle);
    xTaskCreate(web_task,   "WEBSERVER",             1024 * 6,     NULL, 1, &webServerHandle);
}

void loop(){
    delay(1000);
}